summaryrefslogtreecommitdiffstats
path: root/src/Mobs/Monster.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mobs/Monster.cpp')
-rw-r--r--src/Mobs/Monster.cpp354
1 files changed, 82 insertions, 272 deletions
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index d1173c41c..3f7153fb3 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -14,7 +14,7 @@
#include "../Chunk.h"
#include "../FastRandom.h"
-#include "Path.h"
+#include "PathFinder.h"
@@ -75,11 +75,8 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_EMState(IDLE)
, m_EMPersonality(AGGRESSIVE)
, m_Target(nullptr)
- , m_Path(nullptr)
- , m_IsFollowingPath(false)
+ , m_PathFinder(a_Width, a_Height)
, m_PathfinderActivated(false)
- , m_GiveUpCounter(0)
- , m_TicksSinceLastPathReset(1000)
, m_LastGroundHeight(POSY_TOINT)
, m_JumpCoolDown(0)
, m_IdleInterval(0)
@@ -125,127 +122,20 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
-bool cMonster::TickPathFinding(cChunk & a_Chunk)
+void cMonster::MoveToWayPoint(cChunk & a_Chunk)
{
- if (!m_PathfinderActivated)
- {
- return false;
- }
- if (m_TicksSinceLastPathReset < 1000)
- {
- // No need to count beyond 1000. 1000 is arbitary here.
- ++m_TicksSinceLastPathReset;
- }
-
- if (ReachedFinalDestination())
+ if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS)
{
- StopMovingToPosition();
- return false;
- }
-
- if ((m_FinalDestination - m_PathFinderDestination).Length() > 0.25) // if the distance between where we're going and where we should go is too big.
- {
- /* If we reached the last path waypoint,
- Or if we haven't re-calculated for too long.
- Interval is proportional to distance squared, and its minimum is 10.
- (Recalculate lots when close, calculate rarely when far) */
- if (
- ((GetPosition() - m_PathFinderDestination).Length() < 0.25) ||
- ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength())))
- )
- {
- /* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating.
- This is a workaround till we get better path recalculation. */
- if (!m_NoPathToTarget)
- {
- ResetPathFinding();
- }
- }
- }
-
- if (m_Path == nullptr)
- {
- if (!EnsureProperDestination(a_Chunk))
- {
- StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
- return false;
- }
- m_GiveUpCounter = 40;
- m_NoPathToTarget = false;
- m_NoMoreWayPoints = false;
- m_PathFinderDestination = m_FinalDestination;
- m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight());
- }
-
- switch (m_Path->Step(a_Chunk))
- {
- case ePathFinderStatus::NEARBY_FOUND:
- {
- m_NoPathToTarget = true;
- m_PathFinderDestination = m_Path->AcceptNearbyPath();
- break;
- }
-
- case ePathFinderStatus::PATH_NOT_FOUND:
- {
- StopMovingToPosition(); // Try to calculate a path again.
- // Note that the next time may succeed, e.g. if a player breaks a barrier.
- break;
- }
- case ePathFinderStatus::CALCULATING:
- {
- // Pathfinder needs more time
- break;
- }
- case ePathFinderStatus::PATH_FOUND:
- {
- if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
- {
- if (m_EMState == ATTACKING)
- {
- ResetPathFinding(); // Try to calculate a path again.
- // This results in mobs hanging around an unreachable target (player).
- }
- else
- {
- StopMovingToPosition(); // Find a different place to go to.
- }
- return false;
- }
- else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
- {
- if ((m_Path->IsFirstPoint() || ReachedNextWaypoint()))
- {
- m_NextWayPointPosition = m_Path->GetNextPoint();
- m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
- }
- }
- else
- {
- m_NoMoreWayPoints = true;
- }
-
- m_IsFollowingPath = true;
- return true;
- }
+ return;
}
- return false;
-}
-
-
-
-
-void cMonster::MoveToWayPoint(cChunk & a_Chunk)
-{
if (m_JumpCoolDown == 0)
{
if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
{
if (
- (IsOnGround() && (GetSpeedX() == 0.0f) && (GetSpeedY() == 0.0f)) ||
- (IsSwimming() && (m_GiveUpCounter < 15))
+ (IsOnGround() && (GetSpeedX() == 0.0f) && (GetSpeedY() == 0.0f)) // TODO water handling?
)
{
m_bOnGround = false;
@@ -296,98 +186,7 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk)
-bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
-{
- cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z));
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
-
- if ((Chunk == nullptr) || !Chunk->IsValid())
- {
- return false;
- }
-
- int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
- int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
-
- // If destination in the air, first try to go 1 block north, or east, or west.
- // This fixes the player leaning issue.
- // If that failed, we instead go down to the lowest air block.
- Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
- if (!cBlockInfo::IsSolid(BlockType))
- {
- bool InTheAir = true;
- int x, z;
- for (z = -1; z <= 1; ++z)
- {
- for (x = -1; x <= 1; ++x)
- {
- if ((x == 0) && (z == 0))
- {
- continue;
- }
- Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
- if ((Chunk == nullptr) || !Chunk->IsValid())
- {
- return false;
- }
- RelX = FloorC(m_FinalDestination.x + x) - Chunk->GetPosX() * cChunkDef::Width;
- RelZ = FloorC(m_FinalDestination.z + z) - Chunk->GetPosZ() * cChunkDef::Width;
- Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
- if (cBlockInfo::IsSolid(BlockType))
- {
- m_FinalDestination.x += x;
- m_FinalDestination.z += z;
- InTheAir = false;
- goto breakBothLoops;
- }
- }
- }
- breakBothLoops:
-
- // Go down to the lowest air block.
- if (InTheAir)
- {
- while (m_FinalDestination.y > 0)
- {
- Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
- if (cBlockInfo::IsSolid(BlockType))
- {
- break;
- }
- m_FinalDestination.y -= 1;
- }
- }
- }
-
- // If destination in water, go up to the highest water block.
- // If destination in solid, go up to first air block.
- bool InWater = false;
- while (m_FinalDestination.y < cChunkDef::Height)
- {
- Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta);
- if (BlockType == E_BLOCK_STATIONARY_WATER)
- {
- InWater = true;
- }
- else if (cBlockInfo::IsSolid(BlockType))
- {
- InWater = false;
- }
- else
- {
- break;
- }
- m_FinalDestination.y += 1;
- }
- if (InWater)
- {
- m_FinalDestination.y -= 1;
- }
-
- return true;
-}
@@ -406,22 +205,6 @@ void cMonster::MoveToPosition(const Vector3d & a_Position)
void cMonster::StopMovingToPosition()
{
m_PathfinderActivated = false;
- ResetPathFinding();
-}
-
-
-
-
-
-void cMonster::ResetPathFinding(void)
-{
- m_TicksSinceLastPathReset = 0;
- m_IsFollowingPath = false;
- if (m_Path != nullptr)
- {
- delete m_Path;
- m_Path = nullptr;
- }
}
@@ -435,7 +218,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_Health <= 0)
{
- // The mob is dead, but we're still animating the "puff" they leave when they die.
+ // The mob is dead, but we're still animating the "puff" they leave when they die
m_DestroyTimer += a_Dt;
if (m_DestroyTimer > std::chrono::seconds(1))
{
@@ -453,34 +236,57 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_Target = nullptr;
}
- if (GetPosY() >= 0)
+ // Process the undead burning in daylight.
+ HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
+
+ bool a_IsFollowingPath = false;
+ if (m_PathfinderActivated)
{
- // Process the undead burning in daylight.
- HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
- if (TickPathFinding(*Chunk))
+ if (ReachedFinalDestination())
{
- /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
- 1. I am idle
- 2. I was not hurt by a player recently.
- Then STOP. */
- if (
- m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
- WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
- !WouldBurnAt(GetPosition(), *Chunk)
- )
- {
- // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
- StopMovingToPosition();
- m_GiveUpCounter = 40; // This doesn't count as giving up, keep the giveup timer as is.
- }
- else
+ StopMovingToPosition(); // Simply sets m_PathfinderActivated to false.
+ }
+ else
+ {
+ // Note that m_NextWayPointPosition is actually returned by GetNextWayPoint)
+ switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, m_EMState == IDLE ? true : false))
{
- MoveToWayPoint(*Chunk);
+ case ePathFinderStatus::PATH_FOUND:
+ {
+ /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
+ 1. I am idle
+ 2. I was not hurt by a player recently.
+ Then STOP. */
+ if (
+ m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
+ WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
+ !WouldBurnAt(GetPosition(), *Chunk)
+ )
+ {
+ // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
+ StopMovingToPosition();
+ }
+ else
+ {
+ a_IsFollowingPath = true; // Used for proper body / head orientation only.
+ MoveToWayPoint(*Chunk);
+ }
+ break;
+ }
+ case ePathFinderStatus::PATH_NOT_FOUND:
+ {
+ StopMovingToPosition();
+ break;
+ }
+ default:
+ {
+
+ }
}
}
}
- SetPitchAndYawFromDestination();
+ SetPitchAndYawFromDestination(a_IsFollowingPath);
HandleFalling();
switch (m_EMState)
@@ -522,24 +328,11 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-void cMonster::SetPitchAndYawFromDestination()
+void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
{
- Vector3d FinalDestination = m_FinalDestination;
- if (m_Target != nullptr)
- {
- if (m_Target->IsPlayer())
- {
- FinalDestination.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1;
- }
- else
- {
- FinalDestination.y = m_Target->GetPosY() + GetHeight();
- }
- }
-
-
+ /* Todo Buggy */
Vector3d BodyDistance;
- if (!m_IsFollowingPath && (m_Target != nullptr))
+ if (!a_IsFollowingPath && (m_Target != nullptr))
{
BodyDistance = m_Target->GetPosition() - GetPosition();
}
@@ -552,22 +345,39 @@ void cMonster::SetPitchAndYawFromDestination()
VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch);
SetYaw(BodyRotation);
- Vector3d Distance = FinalDestination - GetPosition();
+ Vector3d HeadDistance;
+ if (m_Target != nullptr)
{
- double HeadRotation, HeadPitch;
- Distance.Normalize();
- VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
- if (std::abs(BodyRotation - HeadRotation) < 90)
+ if (m_Target->IsPlayer()) // Look at a player
{
- SetHeadYaw(HeadRotation);
- SetPitch(-HeadPitch);
+ HeadDistance = m_Target->GetPosition() - GetPosition();
+ // HeadDistance.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1;
}
- else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking.
+ else // Look at some other entity
{
- SetHeadYaw(BodyRotation);
- SetPitch(-BodyPitch);
+ HeadDistance = m_Target->GetPosition() - GetPosition();
+ // HeadDistance.y = m_Target->GetPosY() + GetHeight();
}
}
+ else // Look straight
+ {
+ HeadDistance = BodyDistance;
+ HeadDistance.y = 0;
+ }
+
+ double HeadRotation, HeadPitch;
+ HeadDistance.Normalize();
+ VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch);
+ if (std::abs(BodyRotation - HeadRotation) < 90)
+ {
+ SetHeadYaw(HeadRotation);
+ SetPitch(-HeadPitch);
+ }
+ else
+ {
+ SetHeadYaw(BodyRotation);
+ SetPitch(-BodyPitch);
+ }
}
@@ -806,7 +616,7 @@ void cMonster::EventLosePlayer(void)
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
{
- if (m_IsFollowingPath)
+ if (m_PathfinderActivated)
{
return; // Still getting there
}